import cv2
from datetime import datetime
from insightface.app import FaceAnalysis
from sklearn.metrics.pairwise import cosine_similarity
import DB.PDBC as db
from sqlalchemy import text
import hashlib

# 初始化 InsightFace 应用
app = FaceAnalysis()  # 使用 GPU，若无 GPU 可移除 providers 参数
app.prepare(ctx_id=0, det_size=(640, 640))

# 注册的人脸数据库
registered_faces = []

def register_face(img_path, RECOGNITION_CODE):
    """
    注册人脸库
    :param img_path:
    :param RECOGNITION_CODE:
    :return:
    """

    # 读取图像
    img = cv2.imread(img_path)
    # 检测人脸并获取特征向量
    faces = app.get(img)
    if faces:
        embedding = faces[0].embedding
        confidence = getattr(faces[0], 'confidence', None) or getattr(faces[0], 'det_score', None)
        if confidence > 0.85:
            print(img_path, confidence)
            registered_faces.append({
                "embedding":embedding,
                "RECOGNITION_CODE":RECOGNITION_CODE
            })
            return True

    return False




def generate_md5(input_string):
    # 创建一个md5对象
    md5_hash = hashlib.md5()

    # 更新哈希对象的内容，必须是bytes类型
    md5_hash.update(input_string.encode('utf-8'))

    # 获取十六进制的哈希值字符串
    md5_result = md5_hash.hexdigest()

    return md5_result



def recognize_face(img_path,index):
    """
    在注册的人脸库中对比
    :param img_path:
    :param index:
    :return:
    """
    # 读取图像
    img = cv2.imread(img_path)
    # 检测人脸并获取特征向量
    faces = app.get(img)
    if not faces:
        return {
        "faceNum": len(faces)
        }
    embedding = faces[index].embedding
    # 找出最匹配的人脸
    max_sim = 0.0
    RECOGNITION_CODE = None
    for row in registered_faces:
        embedding = row['embedding']
        sim = cosine_similarity([embedding], [embedding])[0][0]
        if sim > max_sim:
            max_sim = sim
            RECOGNITION_CODE = row['RECOGNITION_CODE']

    return {
        "RECOGNITION_CODE":RECOGNITION_CODE,
        "max_sim":max_sim,
        "faceNum":len(faces)
    }

def getImgPath(groupId,imageIndex):

    sys_path = "G:\\04 buondua"

    # 将输入的数字转换为字符串，以便于提取前缀
    input_str = str(groupId)

    # 提取前缀部分，即输入数字的前几位（在这里是前两位，但可以根据需要调整）
    # 这里使用切片操作[:-2]然后转换为int再*100来获取前缀对应的起始值，
    # 但实际上更简单的方法是直接切片后加'00'再转换为int。
    # 不过为了展示多种方法，这里先用一种稍显复杂的方式。
    # 注意：这种方法假设输入数字至少有两位，如果可能有一位的情况需要额外处理。
    prefix_int = int(input_str[:-2]) * 100  # 提取前两位并转换为对应的百位数



    # 计算范围的下限和上限
    lower_bound = prefix_int
    upper_bound = prefix_int + 99

    # 返回结果，格式化为字符串
    reStr = f"{sys_path}\\{lower_bound} - {upper_bound}\\{groupId}\\{imageIndex}.jpg"
    return reStr

if __name__ == '__main__':

    sql = """
        select * from face_table a 
    """
    con = db.connect()
    result = con.execute(text(sql)).fetchall()
    for row in result:
        imgPath = getImgPath(row[2],row[3])
        register_face(imgPath, row[0])

    print("\r\n\r\nRegistered ALL\r\n\r\n")


    sql = """
            select group_id , group_num from meitu_database b where b.group_id in (select DISTINCT GROUP_ID from image_groups a) and scan_face is null  order by b.update_date limit 1
        """
    while True:
        result = con.execute(text(sql)).fetchone()
        con.execute(text("update meitu_database set scan_face = 1 where group_id = :group_id"), {"group_id": result[0]})
        con.execute(text("delete from face_list_table where IMAGE_GROUP = :group_id"), {"group_id": result[0]})

        for index in range(1,(result[1] + 1)):
            try:
                test_img_path = getImgPath(result[0], index)
                print(test_img_path)
                res = recognize_face(test_img_path, 0)
                print(res)
                if res['faceNum'] == 1:
                    if res['max_sim'] > 0.60:
                        print(f"匹配成功 根据编码保存人脸 groupId：{result[0]} index:{index} max_sim：{res['max_sim']} RECOGNITION_CODE：{res['RECOGNITION_CODE']}")
                        con.execute(text("insert into face_list_table values (:RECOGNITION_CODE,:IMAGE_GROUP,:IMAGE_INDEX,:DATE)"),
                                    {"RECOGNITION_CODE":res['RECOGNITION_CODE'],
                                     "IMAGE_GROUP":result[0],
                                     "IMAGE_INDEX":index,
                                     "DATE":datetime.now()})

                    else:
                        imgPath = getImgPath(result[0], index)
                        RECOGNITION_CODE = generate_md5(imgPath)
                        if register_face(imgPath, RECOGNITION_CODE):
                            print(f"匹配失败 插入并注册新人脸 groupId：{result[0]} index:{index} max_sim：{res['max_sim']} RECOGNITION_CODE：{RECOGNITION_CODE}")
                            con.execute(text("insert into face_table values (:RECOGNITION_CODE,:NAME,:IMAGE_GROUP,:IMAGE_INDEX)"),
                                        {"RECOGNITION_CODE":RECOGNITION_CODE,
                                         "NAME":imgPath,
                                         "IMAGE_GROUP":result[0],
                                         "IMAGE_INDEX":index})
                            con.execute(text("update meitu_database set scan_face = null where group_id = :group_id"),{"group_id": result[0]})
                            break
            except Exception as e:
                print(e)

        con.commit()

        #break

    con.close()

